home *** CD-ROM | disk | FTP | other *** search
- {*********************************************************}
- {* AAStStk *}
- {* Copyright (c) Julian M Bucknall 1998-1999 *}
- {* All rights reserved. *}
- {*********************************************************}
- {* String stack (based on ideas from GNU's ObStack) *}
- {*********************************************************}
-
- {Note: this unit is released as freeware. In other words, you are free
- to use this unit in your own applications, however I retain all
- copyright to the code. JMB}
-
- unit AAStStk;
-
- interface
-
- uses
- SysUtils;
-
- type
- PaaString255 = ^TaaString255;
- TaaString255 = string[255];
-
- type
- TaaStringStack = class
- private
- FChunkSize : integer;
- FChunk : PChar;
- FCount : integer;
- FCurString : PChar;
- protected
- function ssGetChunkCount : integer;
- function ssGetSlackSpace : integer;
- procedure ssAddNewChunk;
- public
- constructor Create(aChunkSize : integer);
- {-create the short string stack}
- destructor Destroy; override;
- {-destroy the short string stack; releasing all memory}
-
- procedure Clear;
- {-remove all strings from stack}
- function Examine : TaaString255;
- {-return the top string from the stack without popping it}
- function IsEmpty : boolean;
- {-is the stack empty?}
- function Pop : TaaString255;
- {-pop the top string from the stack; return it}
- function Push(const aSt : TaaString255) : PaaString255;
- {-push the given string onto the stack; return its address on
- the stack}
-
- property Count : integer read FCount;
- {-the number of strings on the stack}
-
- property ChunkCount : integer read ssGetChunkCount;
- property ChunkSize : integer read FChunkSize;
- property SlackSpace : integer read ssGetSlackSpace;
- end;
-
- implementation
-
- {Notes: FCurString acts as the stack pointer.
- To push a string, FCurString is advanced past the current
- string, and aligned to the nearest 4-byte boundary. The
- routine then checks to see if the string being pushed can be
- added to the remaining space in the chunk. If so, it is. If
- not, a new chunk is allocated and the new string is added to
- the beginning of that. The position of the new string on the
- stack is returned.
- To pop a string, the current string is returned and FCurString
- is moved back to the previous string. If that is in another
- chunk, the chunk just vacated is freed.}
-
- type
- PChunkHeader = ^TChunkHeader;
- TChunkHeader = packed record
- chLimit : PChar;
- chPrev : PChunkHeader;
- end;
-
- type
- PStringNode = ^TStringNode;
- TStringNode = packed record
- snPrev : PStringNode;
- snString : TaaString255;
- end;
-
- const
- {the minimum for the chunk size will hold a full 256 byte short
- string, as well as the string node and chunk overhead}
- MinChunkSize = 256 + sizeof(TChunkHeader) + sizeof(pointer);
-
- {===TaaStringStack===================================================}
- constructor TaaStringStack.Create(aChunkSize : integer);
- begin
- inherited Create;
- if (aChunkSize < MinChunkSize) then
- aChunkSize := MinChunkSize;
- FChunkSize := aChunkSize;
- end;
- {--------}
- destructor TaaStringStack.Destroy;
- begin
- Clear;
- inherited Destroy;
- end;
- {--------}
- procedure TaaStringStack.Clear;
- var
- Chunk : PChunkHeader;
- Temp : PChunkHeader;
- begin
- Temp := PChunkHeader(FChunk);
- while (Temp <> nil) do begin
- Chunk := Temp^.chPrev;
- FreeMem(Temp, (Temp^.chLimit - PChar(Temp)));
- Temp := Chunk;
- end;
- FChunk := nil;
- FCurString := nil;
- FCount := 0;
- end;
- {--------}
- function TaaStringStack.Examine : TaaString255;
- begin
- {check for the obvious mistake}
- if (FCurString = nil) then
- raise Exception.Create('TaaStringStack.Examine: the stack is empty');
- {return the current string}
- Result := PStringNode(FCurString)^.snString;
- end;
- {--------}
- function TaaStringStack.IsEmpty : boolean;
- begin
- Result := (FCurString = nil);
- end;
- {--------}
- function TaaStringStack.Pop : TaaString255;
- var
- Temp : PChar;
- begin
- {check for the obvious mistake}
- if (FCurString = nil) then
- raise Exception.Create('TaaStringStack.Pop: the stack is empty');
- {return the current string}
- Result := PStringNode(FCurString)^.snString;
- {move the current string pointer back, checking for switching
- chunks where we need to free the chunk just left}
- if (FChunk + sizeof(TChunkHeader) = FCurString) then begin
- {we're leaving this chunk; set the current string pointer}
- FCurString := PChar(PStringNode(FCurString)^.snPrev);
- {reset the chunk address and dispose of the one just left}
- Temp := FChunk;
- FChunk := PChar(PChunkHeader(FChunk)^.chPrev);
- FreeMem(Temp, FChunkSize);
- end
- else begin
- {just move the current string pointer back}
- FCurString := PChar(PStringNode(FCurString)^.snPrev);
- end;
- dec(FCount);
- end;
- {--------}
- function TaaStringStack.Push(const aSt : TaaString255) : PaaString255;
- var
- PrevNode : PStringNode;
- NewCurString : PChar;
- begin
- {save the current string node address}
- PrevNode := PStringNode(FCurString);
- {check for an empty stack}
- if (FCurString = nil) then begin
- if (FChunk = nil) then
- ssAddNewChunk;
- end
- else begin
- {advance the current string pointer}
- NewCurString := PChar(PrevNode) +
- sizeof(pointer) +
- length(PrevNode^.snString) +
- 2 {the length byte and the hidden end null};
- {align the new pointer}
- NewCurString := pointer((longint(NewCurString) + 3) and $FFFFFFFC);
- {if there's not enough room for the new string, get a new chunk}
- if (PChunkHeader(FChunk)^.chLimit - NewCurString) <
- (sizeof(pointer) + length(aSt) + 2) then
- ssAddNewChunk
- {otherwise, position the current string pointer}
- else
- FCurString := NewCurString;
- end;
- {set up the new node}
- with PStringNode(FCurString)^ do begin
- snPrev := PrevNode;
- snString := aSt;
- snString[length(aSt)+1] := #0;
- end;
- {return address of the pushed string}
- Result := PaaString255(FCurString + sizeof(pointer));
- inc(FCount);
- end;
- {--------}
- procedure TaaStringStack.ssAddNewChunk;
- var
- NewChunk : PChar;
- begin
- GetMem(NewChunk, FChunkSize);
- PChunkHeader(NewChunk)^.chLimit := NewChunk + FChunkSize;
- PChunkHeader(NewChunk)^.chPrev := PChunkHeader(FChunk);
- FChunk := NewChunk;
- FCurString := NewChunk + sizeof(TChunkHeader);
- end;
- {--------}
- function TaaStringStack.ssGetChunkCount : integer;
- var
- Temp : PChunkHeader;
- begin
- Result := 0;
- Temp := PChunkHeader(FChunk);
- while (Temp <> nil) do begin
- inc(Result);
- Temp := Temp^.chPrev;
- end;
- end;
- {--------}
- function TaaStringStack.ssGetSlackSpace : integer;
- var
- Temp : PChunkHeader;
- Next : PChunkHeader;
- Node : PStringNode;
- StartSlack : PChar;
- begin
- Result := 0;
- if (FChunk <> nil) then begin
- Next := PChunkHeader(FChunk);
- Temp := Next^.chPrev;
- while (Temp <> nil) do begin
- Node := PStringNode(PChar(Next) + sizeof(TChunkHeader))^.snPrev;
- StartSlack := PChar(Node) + sizeof(pointer) +
- length(Node^.snString) + 2;
- inc(Result, Temp^.chLimit - StartSlack);
- Next := Temp;
- Temp := Temp^.chPrev;
- end;
- end;
- end;
- {====================================================================}
-
- end.
-